﻿// essentially a direct port of VB
// todo: make it neat

using Gargoyle.Roton;
using Gargoyle.ZZT;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Lyon
{
	public partial class Main : Form
	{
		private Audio Audio;
		private string CurrentName;
		private Encoding Encoding = Encoding.GetEncoding(437);
		private string ErrorMessage;
		private string ErrorStackTrace;
		private string GameFile;
		private BufferInput Input;
		private PlatformID Platform;
        private bool Smoothing = false;
		private bool SuperSpeed = false;
        private EventTimer Timer;
		private TextModeVideo Video;
		private bool VideoBlink;
		private int VideoTick;
		private System.Timers.Timer VideoTimer;
		private Game ZZT;
		private Int16 EngineID;
		private bool WideMode;

		public Main()
		{
			InitializeComponent();
			DoubleClick += GameFormDoubleClick;
			KeyDown += GameFormKeyDown;
			Shown += GameFormShown;
			
			Platform = System.Environment.OSVersion.Platform;
		}

		private void DisplayError(object sender)
		{
			StringBuilder builder = new StringBuilder();
			builder.AppendLine("Roton has crashed:");
			builder.AppendLine(ErrorMessage);
			builder.AppendLine();
			builder.Append("Would you like a stack trace copied to your clipboard?");
			if (MessageBox.Show(builder.ToString(), "Crash", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
			{
				Clipboard.SetText(ErrorStackTrace);
			}
		}

		private void GameFormDoubleClick(object sender, EventArgs e)
		{
			using (SaveFileDialog SFD = new SaveFileDialog())
			{
				SFD.AddExtension = true;
				SFD.AutoUpgradeEnabled = true;
                SFD.Filter = "PNG Screenshots (*.png)|*.png;*.PNG";
				SFD.FilterIndex = 1;
				SFD.OverwritePrompt = true;
				SFD.Title = "Save Screenshot";
				if (SFD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
				{
					BackgroundImage.Save(SFD.FileName, System.Drawing.Imaging.ImageFormat.Png);
				}
			}
		}

		private void GameFormKeyDown(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.I:
					ZZT.ToggleFlag("I");
					break;
                case Keys.D1:
                    if(e.Control)
                        this.ClientSize = new Size(Video.Bitmap.Width, Video.Bitmap.Height);
                    break;
                case Keys.D2:
                    if(e.Control)
                        this.ClientSize = new Size(Video.Bitmap.Width * 2, Video.Bitmap.Height * 2);
                    break;
                case Keys.Oemtilde:
                    if(e.Control)
                        Smoothing = !Smoothing;
                    break;
				default:
					break;
			}
			Input.KeyDown(e.KeyData);
		}

		private Int16 GetWorldID(byte[] bytes)
		{
			using (MemoryStream mem = new MemoryStream(bytes))
			{
				BinaryReader reader = new BinaryReader(mem);
				return reader.ReadInt16();
			}
		}

		private void InitTimer()
		{
			if (Timer != null)
			{
				Timer.Stop();
				Timer = null;
			}
			if (SuperSpeed)
                Timer = new EventTimer(Tick, double.MaxValue, int.MaxValue);
			else
                Timer = new EventTimer(Tick, 71.2d, 30);
		}

		private Cache LoadWorld()
		{
			string FileName;
			Cache Result;
			using (OpenFileDialog OFD = new OpenFileDialog())
			{
				OFD.AddExtension = true;
				OFD.AutoUpgradeEnabled = true;
				OFD.CheckFileExists = true;
				OFD.CheckPathExists = true;
                OFD.Filter = "World Files (*.zzt, *.szt)|*.zzt;*.szt;*.ZZT;*.SZT";
				OFD.FilterIndex = 1;
				OFD.Multiselect = false;
				OFD.ShowHelp = false;
				OFD.ShowReadOnly = false;
				OFD.SupportMultiDottedExtensions = true;
				OFD.Title = "Open World";
				OFD.ValidateNames = true;

				if (OFD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
				{
					FileName = OFD.FileName;
					if (FileName != "")
					{
						byte[] FileData = File.ReadAllBytes(FileName);
						EngineID = GetWorldID(FileData);
						WideMode = (EngineID == -2);
						Result = new Cache(Path.GetDirectoryName(FileName));
						Result.SaveFile(Path.GetFileName(FileName), FileData);
						GameFile = Path.GetFileNameWithoutExtension(FileName);
						return Result;
					}
				}

				return null;
			}
		}

		protected override void OnClosed(EventArgs e)
		{
			if (Timer != null)
				Timer.Dispose();
			base.OnClosed(e);
		}

		protected override void OnPaintBackground(PaintEventArgs e)
		{
            if(Smoothing)
                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
            else
                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

			e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
			base.OnPaintBackground(e);
		}

		[STAThread()]
		private void Tick(object state, EventArgs e)
		{
			if (ZZT != null && ZZT.Running && !ZZT.Busy)
			{
				if (Debugger.IsAttached)
					ZZT.Execute();
				else
				{
					try
					{
						ZZT.Execute();
					}
					catch (Exception ex)
					{
						Timer.Stop();
						ErrorMessage = ex.Message;
						ErrorStackTrace = ex.StackTrace;
						Thread ErrorThread = new Thread(new ParameterizedThreadStart(DisplayError));
						ErrorThread.SetApartmentState(ApartmentState.STA);
						ErrorThread.Start();
						ZZT = null;
					}
				}
			}
		}

		private void UpdateScreen(object sender, System.Timers.ElapsedEventArgs e)
		{
			string GameName;
			bool PerformUpdate = false;

			if (ZZT == null || !ZZT.Running)
			{
				Close();
				return;
			}

			GameName = ZZT.Name();
			if (GameName != CurrentName)
			{
				CurrentName = GameName;
				Text = "Lyon - [" + GameName + "]";
			}

			VideoTick++;
			if ((VideoTick % 15) == 0)
			{
				VideoBlink = !VideoBlink;
				Video.SetBlink(VideoBlink);
				VideoTick = 0;
				PerformUpdate = true;
			}

			PerformUpdate |= ZZT.UpdateScreen;

			if (PerformUpdate)
				Invalidate();
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
		}

		private void GameFormShown(object sender, EventArgs e)
		{
			Cache InitCache = LoadWorld();
			
			if (InitCache == null)
				Close();
			else
			{
				int VideoWidth = 80;
				if (WideMode)
					VideoWidth /= 2;
				Video = new TextModeVideo(new Size(VideoWidth, 25), null, null, WideMode);
				Input = new BufferInput();
                Audio = new AudioOpenAL(44100, Gargoyle.Roton.Audio.BitDepth.Signed16Bit, 2048);
                ZZT = new Game(Video, Audio, Input, InitCache, EngineID, true);
				ClientSize = new Size(Video.Bitmap.Width, Video.Bitmap.Height);
				BackgroundImage = Video.Bitmap;
				InitTimer();
				VideoTimer = new System.Timers.Timer(1);
				VideoTimer.SynchronizingObject = this;
				ZZT.Load(GameFile);
				Timer.Start();
				VideoTimer.Start();
				VideoTimer.Elapsed += UpdateScreen;
			}
		}
	}
}
